using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using TemplatesDatabase;
using Random = Game.Random;

namespace SCIENEW {
    public class ComponentSifter : ComponentInventoryBase, ICrafterComponent, IDrawable {
        public int RemainsSlotIndex => -1;
        public int ResultSlotIndex => -1;
        public static int RequiredHeatLevel => -6;
        public int[] DrawOrders => [1];
        public PrimitivesRenderer3D m_primitivesRenderer3D = new();
        public ComponentBlockEntity m_componentBlockEntity;
        public SubsystemAudio m_subsystemAudio;
        public SubsystemSoundMaterials m_subsystemSoundMaterials;
        public SubsystemPickables m_subsystemPickables;
        public SubsystemTerrain m_subsystemTerrain;
        public SubsystemParticles m_subsystemParticles;
        public CraftingRecipe m_recipe;
        public DrawBlockEnvironmentData m_drawBlockEnvironmentData = new();
        public float m_progress = 0;
        public double m_lastSiftTime = 0;

        public override int GetSlotCapacity(int slotIndex, int value) {
            CraftingRecipe craftingRecipe = FindRecipe(value);
            if (craftingRecipe != null) {
                m_recipe = craftingRecipe;
                return 1;
            }
            m_recipe = null;
            return 0;
        }

        public override void AddSlotItems(int slotIndex, int value, int count) {
            base.AddSlotItems(slotIndex, value, count);
        }

        public bool Sift() {
            int blockValue = GetSlotValue(0);
            if (blockValue == 0) return false;

            //添加进度
            if (Time.RealTime - m_lastSiftTime > 0.2) {
                m_lastSiftTime = Time.RealTime;
                m_progress += 0.05f;
                m_subsystemSoundMaterials.PlayImpactSound(blockValue, new Vector3(m_componentBlockEntity.Coordinates), 1);
            }

            //添加粒子
            Block block = BlocksManager.Blocks[Terrain.ExtractContents(GetSlotValue(0))];
            m_subsystemParticles.AddParticleSystem(block.CreateDebrisParticleSystem(m_subsystemTerrain, new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f, 0f, 0.5f), GetSlotValue(0), 0.5f));

            //检测完成度
            if (m_progress >= 1f) {
                m_progress = 0f;
                m_slots[0].Count = 0;
                m_slots[0].Value = 0;

                //根据配方里物品的数量获取掉落概率
                Dictionary<int[], float> resultsCount = new();
                HashSet<string> results = m_recipe.Ingredients.Where(x => x != null).ToHashSet();
                int maxCount = -1;
                foreach (var result in results) {
                    int count = m_recipe.Ingredients.Count(x => x == result);
                    resultsCount.Add(IECraftingRecipesManager.FindBlocksByCraftingId(result), count);
                    maxCount = Math.Max(maxCount, count);
                }
                //掉落物品
                Random random = new();
                float randomValue = random.Float();

                foreach (var result in resultsCount) {
                    float probability = result.Value / maxCount;
                    if (probability >= randomValue) {
                        int resultValue = result.Key[new Random().Int(result.Key.Length)];
                        Vector3 pos = new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f, 1f, 0.5f);
                        m_subsystemPickables.AddPickable(
                            resultValue,
                            1,
                            pos,
                            new Random().Float(1f, 1.5f) * Vector3.Normalize(new Vector3(new Random().Float(-1f, 1f), new Random().Float(1f, 2f), new Random().Float(-1f, 1f))),
                            null
                        );
                    }
                }
            }
            return true;
        }

        public CraftingRecipe FindRecipe(int mainMaterial) {
            foreach (var recipe in CraftingRecipesManager.Recipes) {
                if (recipe.ResultValue == mainMaterial
                    && recipe.RequiredHeatLevel == RequiredHeatLevel)
                    return recipe;
            }
            return null;
        }

        public CraftingRecipe MatchRecipe(SubsystemTerrain terrain, string[] ingredients, float heatLevel, float playerLevel) => throw new NotImplementedException();

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>(true);
            m_subsystemAudio = Project.FindSubsystem<SubsystemAudio>(true);
            m_subsystemSoundMaterials = Project.FindSubsystem<SubsystemSoundMaterials>(true);
            m_subsystemPickables = Project.FindSubsystem<SubsystemPickables>(true);
            m_subsystemParticles = Project.FindSubsystem<SubsystemParticles>(true);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_progress = valuesDictionary.GetValue("Progress", 0f);
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap) {
            base.Save(valuesDictionary, entityToIdMap);
            valuesDictionary.SetValue("Progress", m_progress);
        }

        public void Draw(Camera camera, int drawOrder) {
            if (!DrawingUtils.IsVisible(m_subsystemTerrain, camera, new Vector3(m_componentBlockEntity.Coordinates))) return;

            int blockValue = GetSlotValue(0);
            if (blockValue == 0) return;
            Block block = BlocksManager.Blocks[Terrain.ExtractContents(blockValue)];

            Point3 position = m_componentBlockEntity.Coordinates;
            DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, position);

            Matrix scaleMatrix = Matrix.CreateScale(7 / 8f, (1 - m_progress) * (14f / 32f), 7 / 8f);
            Matrix matrix = scaleMatrix * Matrix.CreateTranslation(new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f) + new Vector3(0, MathUtils.Lerp(9 / 32f, 2 / 32f, m_progress), 0));
            block.DrawBlock(m_primitivesRenderer3D, blockValue, Color.White, 1, ref matrix, m_drawBlockEnvironmentData);
            m_primitivesRenderer3D.Flush(camera.ViewProjectionMatrix);
        }
    }
}